iT邦幫忙

2025 iThome 鐵人賽

DAY 11
2
生成式 AI

AI 給我錢錢錢 ! AI 股神養成計劃系列 第 11

Day 11 : 技術指標 AI 解讀:讓 RSI、MA 變成白話解釋

  • 分享至 

  • xImage
  •  

今天要用最常見、好上手的兩個指標:移動平均線(MA) 與 相對強弱指標(RSI)。並且用 yfinance 抓資料、自己計算 MA 與 RSI,把冷冰冰的數字變成 Gemini 的白話解釋(支援台股/美股)。

觀念速讀

移動平均線(MA)

  • 是什麼:把最近 N 天收盤價取平均,平滑雜訊、觀察趨勢。

  • 常見參數:短天期 5/10/20,長天期 60/120/200。

  • 解讀:

  • 價格在 短均線上方、且短均線 向上 → 多頭動能較強。

  • 黃金交叉:短天期 MA 上穿長天期 MA(常被視為偏多訊號)。

  • 死亡交叉:短天期 MA 下穿長天期 MA(常被視為偏空訊號)。

  • 限制:落後指標(反應趨勢但有延遲,容易被假突破反覆騙線)。

相對強弱指標(RSI, 14)

  • 是什麼:衡量近 14 天上漲與下跌的相對強度。

  • 公式概念:

  • 先算每日漲跌 Δ = Close.diff()

  • 上漲視為 gain、下跌視為 loss

  • Wilder 平滑 算「平均漲/跌」→ RS = avg_gain / avg_loss

  • RSI = 100 - 100 / (1 + RS)

常見區間

Colab 實作:抓資料 → 計算指標 → AI 白話解讀

可以把 TICKER 換成美股如 AAPL、TSLA,或台股如 2330.TW、0050.TW。

安裝與匯入

python
!pip install -q yfinance pandas google-generativeai

import os
import pandas as pd
import numpy as np
import yfinance as yf
import google.generativeai as genai
from datetime import datetime, timedelta

# 設定 Gemini API Key(建議用環境變數)
GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "YOUR_API_KEY_HERE")
genai.configure(api_key=GEMINI_KEY)

MODEL_NAME = "gemini-1.5-flash"  # 速度快、夠便宜

參數與資料取得

python
TICKER = "AAPL"        # 例:AAPL / TSLA / 2330.TW
PERIOD = "6mo"         # 過去 6 個月
SHORT_MA = 20
LONG_MA = 60
RSI_PERIOD = 14

df = yf.download(TICKER, period=PERIOD)
if df.empty:
    raise ValueError(f"抓不到 {TICKER} 的資料,請確認代號是否正確。")

# 只留必需欄
price = df["Close"].dropna().copy()

執行結果

https://ithelp.ithome.com.tw/upload/images/20250825/20169444ad9YPVxiK0.png

計算 MA 與 RSI(Wilder 法)

python
# SMA
sma_short = price.rolling(window=SHORT_MA).mean()
sma_long  = price.rolling(window=LONG_MA).mean()

# RSI (Wilder smoothing)
delta = price.diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)

avg_gain = gain.ewm(alpha=1/RSI_PERIOD, adjust=False).mean()
avg_loss = loss.ewm(alpha=1/RSI_PERIOD, adjust=False).mean()

rs = avg_gain / (avg_loss.replace(0, np.nan))
rsi = 100 - (100 / (1 + rs))
rsi = rsi.fillna(50)  # 初期 NaN 給中性值

print(sma_short.tail())
print(sma_long.tail())
print(rsi.tail())

執行結果

https://ithelp.ithome.com.tw/upload/images/20250825/20169444N5wBl7lmQ0.png

產出當下解讀要用的「事實摘要」

python
latest_date = price.index[-1].strftime("%Y-%m-%d")
last_close  = float(price.iloc[-1])
s_short     = float(sma_short.iloc[-1])
s_long      = float(sma_long.iloc[-1])
rsi_now     = float(rsi.iloc[-1])

# 近 5 日均線斜率(估算趨勢)
def slope(series, lookback=5):
    tail = series.dropna().iloc[-lookback:]
    if len(tail) < lookback:
        return 0.0
    x = np.arange(len(tail))
    A = np.vstack([x, np.ones(len(x))]).T
    m, _ = np.linalg.lstsq(A, tail.values, rcond=None)[0]
    return float(m)

slope_short = slope(sma_short)
slope_long  = slope(sma_long)

# 交叉判斷(近 2 日穿越)
diff_today = float(sma_short.iloc[-1]) - float(sma_long.iloc[-1])
diff_yest  = float(sma_short.iloc[-2]) - float(sma_long.iloc[-2])
cross = "none"

if not np.isnan(diff_today) and not np.isnan(diff_yest):
    if diff_yest <= 0 and diff_today > 0:
        cross = "golden_cross"
    elif diff_yest >= 0 and diff_today < 0:
        cross = "death_cross"


facts = {
    "ticker": TICKER,
    "as_of": latest_date,
    "last_close": round(last_close, 3),
    f"SMA_{SHORT_MA}": round(s_short, 3),
    f"SMA_{LONG_MA}": round(s_long, 3),
    "slope_short": round(slope_short, 5),
    "slope_long": round(slope_long, 5),
    f"RSI_{RSI_PERIOD}": round(rsi_now, 2),
    "ma_cross": cross
}
facts

執行結果

https://ithelp.ithome.com.tw/upload/images/20250825/20169444YlNZ8hU9nA.png

本地規則先給一版「工程師口吻」解讀(可做為 fallback)

python
def humanize_interpretation(f):
    lines = []
    # MA 相對位置
    if f["last_close"] > f[f"SMA_{SHORT_MA}"] > f[f"SMA_{LONG_MA}"]:
        lines.append("價格位於短、長期均線之上,屬於偏多排列。")
    elif f["last_close"] < f[f"SMA_{SHORT_MA}"] < f[f"SMA_{LONG_MA}"]:
        lines.append("價格位於短、長期均線之下,屬於偏空排列。")
    else:
        lines.append("價格與均線多空交錯,短線方向不明確。")

    # 均線斜率
    if f["slope_short"] > 0 and f["slope_long"] > 0:
        lines.append("短長期均線皆上彎,整體趨勢偏多。")
    elif f["slope_short"] < 0 and f["slope_long"] < 0:
        lines.append("短長期均線皆下彎,整體趨勢偏弱。")
    else:
        lines.append("短長期均線方向不一致,留意盤整或轉折。")

    # 交叉
    if f["ma_cross"] == "golden_cross":
        lines.append("出現黃金交叉(短上穿長),常被視為偏多訊號。")
    elif f["ma_cross"] == "death_cross":
        lines.append("出現死亡交叉(短下穿長),常被視為偏空訊號。")

    # RSI
    r = f[f"RSI_{RSI_PERIOD}"]
    if r >= 70:
        lines.append(f"RSI ≈ {r}(偏熱),強勢中但留意回檔風險。")
    elif r <= 30:
        lines.append(f"RSI ≈ {r}(偏冷),超賣中但不等同立刻反彈。")
    else:
        lines.append(f"RSI ≈ {r}(中性區間),動能溫和。")

    return ";".join(lines)

print("工程師口吻解讀:", humanize_interpretation(facts))

執行結果

https://ithelp.ithome.com.tw/upload/images/20250825/20169444BsMlb9vK55.png

交給 Gemini:把數字翻成「白話、人話、好懂」

python
model = genai.GenerativeModel(MODEL_NAME)

prompt = f"""
你是一位投資教練,請把下面的技術指標數據,翻成一般人也能聽懂的「白話解讀」,
限制:避免靠單一指標下結論,提醒可能的誤判與風險,內容 150~250 字,條列 3~5 點重點,最後1行給出「短線觀察重點」。

數據(請勿臆測未提供的值):
{facts}

可參考但不要直接複製的示意句型:
- 價格與均線的相對位置暗示動能方向,但短期可能出現假突破。
- RSI 進入極值不代表一定反轉,強勢趨勢可能長時間維持高檔。
- 交叉訊號在盤整容易洗刷,建議搭配成交量或風險控管。

請用繁體中文輸出。
"""

try:
    res = model.generate_content(prompt)
    print(res.text)
except Exception as e:
    print("Gemini 呼叫失敗,改用本地解讀:")
    print(humanize_interpretation(facts))

執行結果

https://ithelp.ithome.com.tw/upload/images/20250825/20169444vUGscYK3w0.png

該如何運用在網站/報告

  • 將 facts 改成你的後端 API 計算好的 JSON,前端把 JSON 丟給 Gemini 生成解讀文案。

  • 新手模式:輸出白話版;進階模式:輸出專業版(多用術語)。

  • 若 LLM 額度不足,使用 humanize_interpretation 當 fallback,網站依然能工作。

常見誤區 & 小提醒

  • RSI > 70 不是一定賣、RSI < 30 不是一定買:強勢趨勢可能久居極值。

  • 均線交叉在盤整區常常「假訊號」:務必配合風險控管(停損、部位大小)。

  • 參數沒有聖杯:20/60、10/30、50/200 都有人用,請回測/紙上交易驗證。

結語

在今天的練習中,我們學會了如何利用 AI 將艱澀的技術指標(如 RSI、MA)轉換成白話的說明,讓投資新手也能快速掌握市場脈絡。這不僅幫助我們做更清楚的投資判斷,也展現了 AI 在金融教育與輔助決策上的潛力。未來,我們還可以將更多指標、甚至結合多重數據,一步步打造屬於自己的「智慧投資助理」。
👉 明天(Day 12),我們要更進一步——看看 AI 如何讀懂新聞、輿論情緒,幫你判斷市場是樂觀還是悲觀!


上一篇
Day 10 : 用 AI 自動生成公司簡介:輸入股票代號就有說明書
下一篇
Day 12 : 新聞與輿情分析:AI 幫你讀懂市場情緒
系列文
AI 給我錢錢錢 ! AI 股神養成計劃20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言